﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AgorApp.Roma.ServiceContracts;
using AgorApp.Roma.Model;
using AgorApp.Roma.Model.DataTransferObjects;
using AutoMapper;
using AgorApp.Roma.Oracle.Services.Mappers;
using Newtonsoft.Json;
using System.Data.Entity;
using AutoMapper.Mappers;

namespace AgorApp.Roma.Oracle.Services
{
    public class ConsiglieriAssembleaService : IConsiglieriAssembleaService
    {
        public IGruppiConsiliariService GruppiConsiliariService { get; private set; }
        public IConsiglieriImagesService ConsiglieriImagesService { get; private set; }
        public ITagService TagService { get; private set; }

        private AgorAppRomaContext dbContext;
        public ConsiglieriAssembleaService(IGruppiConsiliariService gruppiConsiliariService,
            IConsiglieriImagesService consiglieriImagesService, ITagService tagService)
        {
            this.GruppiConsiliariService = gruppiConsiliariService;
            this.ConsiglieriImagesService = consiglieriImagesService;
            this.TagService = tagService;

            dbContext = new AgorAppRomaContext();
            MapperConfigure();
        }

        #region AutoMapper

        private ConfigurationStore config;
        private MappingEngine mapper;

        private void MapperConfigure()
        {
            config = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
            config.AssertConfigurationIsValid();
            mapper = new MappingEngine(config);
            config.AddProfile<DataTransferObjectsProfile>();
            config.AddProfile<ConsiglieriAssembleaServiceProfile>();
            mapper.ConfigurationProvider.AssertConfigurationIsValid();
        }

        private class ConsiglieriAssembleaServiceProfile : Profile
        {
            public override string ProfileName
            {
                get
                {
                    return this.GetType().Name;
                }
            }

            protected override void Configure()
            {
                base.Configure();

                SourceMemberNamingConvention = new PascalCaseNamingConvention();
                DestinationMemberNamingConvention = new UpperUnderscoreNamingConvention();

                CreateMap<ConsiglieriAssemblea, CONSIGLIERI_ASSEMBLEA>()
                    .ForMember(dest => dest.NOTE, opt => opt.ResolveUsing((ConsiglieriAssemblea src) =>
                    {
                        return JsonConvert.SerializeObject(src.Note);
                    }))
                    .ForMember(dest => dest.GRUPPI_CONSILIARI, opt => opt.Ignore());

                CreateMap<ConsiglieriImages, CONSIGLIERI_IMAGES>();

                CreateMap<Tag, TAG>()
                    .ForMember(dest => dest.TAG1, opt => opt.MapFrom(src => src.TagName))
                    .ForMember(dest => dest.ID_TIPO_TAG, opt => opt.MapFrom(src => (short)src.TagType))
                    .ForMember(dest => dest.TAG_TYPE, opt => opt.Ignore())
                    .ForMember(dest => dest.GRUPPI_CONSILIARI, opt => opt.Ignore())
                    .ForMember(dest => dest.ASSEMBLEE_CAPITOLINE, opt => opt.Ignore())
                    .ForMember(dest => dest.DELIBERE_ASSEMBLEA_CAPITOLINA, opt => opt.Ignore())
                    .ForMember(dest => dest.NEWS, opt => opt.Ignore());
            }
        }
        #endregion

        #region CRUD Operations

        #region Direct methods

        public ICollection<ConsiglieriAssemblea> GetAll()
        {
            var consiglieri = dbContext.CONSIGLIERI_ASSEMBLEA
                .Include(src => src.GRUPPI_CONSILIARI)
                .Include(src => src.CONSIGLIERI_IMAGES)
                .Include(src => src.TAGS);
            return mapper.Map<ICollection<ConsiglieriAssemblea>>(consiglieri);
        }

        public ConsiglieriAssemblea Get(short key)
        {
            var consigliere = dbContext.CONSIGLIERI_ASSEMBLEA.Find(key);
            if (consigliere == null) return null;
            dbContext.Entry(consigliere).Reference(src => src.GRUPPI_CONSILIARI).Load();
            dbContext.Entry(consigliere).Collection(src => src.CONSIGLIERI_IMAGES).Load();
            dbContext.Entry(consigliere).Collection(src => src.TAGS).Load();

            return mapper.Map<ConsiglieriAssemblea>(consigliere);
        }

        public void Insert(ConsiglieriAssemblea entity)
        {
            MapperConfigure();

            var ent = dbContext.CONSIGLIERI_ASSEMBLEA.Find(entity.IdConsigliere);
            if (ent != null)
                throw new InvalidOperationException("Consigliere già presente");

            var consigliere = mapper.Map<ConsiglieriAssemblea, CONSIGLIERI_ASSEMBLEA>(entity, ent);
            if (consigliere.ID_GRUPPO <= 0)
                throw new InvalidOperationException("Consigliere senza gruppo di appartenenza");
            //gestione tags
            var tags = consigliere.TAGS.ToList();
            consigliere.TAGS.Clear();
            foreach (var tag in tags)
            {
                var t = dbContext.TAGS.SingleOrDefault(x => x.TAG1.Equals(tag.TAG1)
                    && x.ID_TIPO_TAG == tag.ID_TIPO_TAG);
                if (t != null)
                    consigliere.TAGS.Add(t);
                else consigliere.TAGS.Add(tag);
            }
            dbContext.CONSIGLIERI_ASSEMBLEA.Add(consigliere);
            dbContext.SaveChanges();
            entity = mapper.Map<CONSIGLIERI_ASSEMBLEA, ConsiglieriAssemblea>(consigliere);
        }

        public void Update(ConsiglieriAssemblea entity)
        {
            MapperConfigure();

            var ent = dbContext.CONSIGLIERI_ASSEMBLEA.Find(entity.IdConsigliere);
            if (ent == null)
                throw new InvalidOperationException("Consigliere inesistente in banca dati");

            ent = mapper.Map<ConsiglieriAssemblea, CONSIGLIERI_ASSEMBLEA>(entity, ent);
            if (ent.ID_GRUPPO <= 0)
                throw new InvalidOperationException("Consigliere non appartenente a nessun gruppo");
            //images
            var images = ent.CONSIGLIERI_IMAGES.ToList();
            ent.CONSIGLIERI_IMAGES.Clear();
            foreach (var image in images)
            {
                var im = dbContext.CONSIGLIERI_IMAGES.Find(image.ID_IMAGE);
                if (im != null)
                {
                    im.ID_CONSIGLIERE = ent.ID_CONSIGLIERE;
                    ent.CONSIGLIERI_IMAGES.Add(im);
                }
                else
                {
                    image.ID_CONSIGLIERE = ent.ID_CONSIGLIERE;
                    ent.CONSIGLIERI_IMAGES.Add(image);
                }
            }
            //tags
            var tags = ent.TAGS.ToList();
            ent.TAGS.Clear();
            foreach (var tag in tags)
            {
                var t = dbContext.TAGS.SingleOrDefault(x => x.TAG1.Equals(tag.TAG1)
                    && x.ID_TIPO_TAG == tag.ID_TIPO_TAG);
                if (t != null)
                    ent.TAGS.Add(t);
                else ent.TAGS.Add(tag);
            }
            //context
            dbContext.CONSIGLIERI_ASSEMBLEA.Add(ent);
            dbContext.SaveChanges();
            //update entity for return
            entity = mapper.Map<CONSIGLIERI_ASSEMBLEA, ConsiglieriAssemblea>(ent, entity);
        }

        public void Delete(ConsiglieriAssemblea entity)
        {
            var ent = dbContext.CONSIGLIERI_ASSEMBLEA.Find(entity.IdConsigliere);
            if (ent == null)
                throw new InvalidOperationException("Consigliere inesistente in banca dati");
            dbContext.CONSIGLIERI_ASSEMBLEA.Remove(ent);
            dbContext.SaveChanges();
        }

        #endregion

        #region Async methods

        public async Task<ICollection<ConsiglieriAssemblea>> GetAllAsync()
        {
            return await Task.Run<ICollection<ConsiglieriAssemblea>>(() =>
                GetAll());
        }

        public async Task<ConsiglieriAssemblea> GetAsync(short key)
        {
            return await Task.Run<ConsiglieriAssemblea>(() =>
                Get(key));
        }

        public async Task InsertAsync(ConsiglieriAssemblea entity)
        {
            await Task.Run(() => Insert(entity));
        }

        public async Task UpdateAsync(ConsiglieriAssemblea entity)
        {
            await Task.Run(() => Update(entity));
        }

        public async Task DeleteAsync(ConsiglieriAssemblea entity)
        {
            await Task.Run(() => Delete(entity));
        } 

        #endregion

        #endregion


        #region Disposable

        private bool disposed = false;
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    dbContext.Dispose();
                    mapper.Dispose();
                }
            }
            disposed = true;
        }

        #endregion
    }
}
